﻿using System.Reflection;
using Microsoft.Extensions.DependencyInjection;

namespace DYH.Extensions.DependencyInjection;

/// <summary>
/// 依赖注入容器工具类
/// </summary>
internal sealed class ServiceCollectionHelper : IServiceCollectionHelper
{
    private readonly IServiceCollection _serviceCollection;

    public ServiceCollectionHelper(IServiceCollection serviceCollection)
    {
        _serviceCollection = serviceCollection;
    }

    public void BeginInject()
    {
        #region 拿到所有类型

        // 从程序集中拿
        HashSet<Type> classSet = [];
        foreach (var assAndWhereExpression in _assVtList)
        {
            var ass = assAndWhereExpression.Assembly;
            var function = assAndWhereExpression.WhereExpression;
            foreach (var type in ass.GetTypes())
            {
                if (type.IsClass && type is not { IsAbstract: true, IsSealed: true })
                {
                    if (type.GetCustomAttribute<DependencyInjectionAttribute>() is null)
                        continue;
                    var add = function is null || function.Invoke(type);
                    if (add)
                    {
                        classSet.Add(type);
                    }
                }
            }
        }
        // 从列表中拿
        foreach (var type in _typeList)
        {
            if (type.IsClass && type is not { IsAbstract: true, IsSealed: true })
            {
                if (type.GetCustomAttribute<DependencyInjectionAttribute>() is not null)
                {
                    classSet.Add(type);
                }
            }
        }

        #endregion

        // 遍历
        foreach (var impType in classSet)
        {
            // 拿到上面的注入特性
            var attributes = impType
                .GetCustomAttributes<DependencyInjectionAttribute>()
                .OrderBy(e => e.InterfaceType is null ? 0 : 1)
                .ToList();
            foreach (var attribute in attributes)
            {
                // 没有特性直接注
                if (attribute.InterfaceType is null)
                {
                    switch (attribute)
                    {
                        case SingletonDIAttribute:
                            _serviceCollection.AddSingleton(impType);
                            break;
                        case ScopedDIAttribute:
                            _serviceCollection.AddScoped(impType);
                            break;
                        case TransientDIAttribute:
                            _serviceCollection.AddTransient(impType);
                            break;
                    }
                }
                // 有特性的注接口
                else
                {
                    var interfaceType = attribute.InterfaceType;
                    switch (attribute)
                    {
                        case SingletonDIAttribute:
                            _serviceCollection.AddSingleton(interfaceType, impType);
                            break;
                        case ScopedDIAttribute:
                            _serviceCollection.AddScoped(interfaceType, impType);
                            break;
                        case TransientDIAttribute:
                            _serviceCollection.AddTransient(interfaceType, impType);
                            break;
                    }
                }
            }
        }
    }

    private readonly List<AssAndWhereExpression> _assVtList = [];
    private readonly List<Type> _typeList = [];

    public IServiceCollectionHelper AddAssembly(IEnumerable<Assembly> assemblies,
        Func<Type, bool>? whereExpression = null)
    {
        _assVtList.AddRange(assemblies.Select(e => new AssAndWhereExpression(e, whereExpression)));
        return this;
    }

    public IServiceCollectionHelper AddTypes(IEnumerable<Type> types)
    {
        _typeList.AddRange(types);
        return this;
    }
}

/// <summary>
/// 条件
/// </summary>
internal sealed class AssAndWhereExpression
{
    public AssAndWhereExpression(Assembly assembly, Func<Type, bool>? whereExpression = null)
    {
        Assembly = assembly;
        WhereExpression = whereExpression;
    }

    public Assembly Assembly { get; }
    public Func<Type, bool>? WhereExpression { get; }
}